// no claim or warranty is made on this code
// it is very brute force 

using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace parseObjectiveC
{
    public class SourceStream : Stream
    {
        Stream source;

        public SourceStream(Stream source)
        {
            this.source = source;
        }

        public override bool CanRead { get { return true; } }
        public override bool CanWrite { get { return false; } }
        public override bool CanSeek { get { return false; } }
        public override long Length { get { return source.Length; } }
        public override long Position { get { throw new Exception(); } set { throw new Exception(); } }
        public override void Flush() { throw new Exception(); }

        public override int Read(byte[] buf, int offset, int count)
        {
            int n = 0;

            for (int i = 0; i < count; i++)
            {
                int c = ReadByte();
                if (c == -1)
                    return n;
                buf[offset + n] = (byte)c;
                n++;
            }
            return n;
        }

        public override long Seek(long p, SeekOrigin o)
        {
            throw new Exception();
        }

        public override void SetLength(long l)
        {
            throw new Exception();
        }

        public override void Write(byte[] b, int a, int c)
        {
            throw new Exception();
        }

        public override int ReadByte()
        {
        restart:
            int n = source.ReadByte();
            if (n == -1)
                return -1;

            if (n == '/')
            {
                int p = source.ReadByte();
                if (p == '/')
                {
                    while (true)
                    {
                        n = source.ReadByte();
                        if (n == -1)
                            return -1;
                        if (n == '\n')
                            return n;
                    }
                }
                else if (p == '*')
                {
                    while (true)
                    {
                        n = source.ReadByte();
                        if (n == -1)
                            return -1;
                        while (n == '*')
                        {
                            n = source.ReadByte();
                            if (n == -1)
                                return -1;
                            if (n == '/')
                                goto restart;
                        }
                    }
                }
                source.Position = source.Position - 1;
                return '/';
            }
            return n;
        }
    }

    class TrivialParser
    {
        StreamWriter gencs;
        StreamWriter enumcs;
        StreamReader r;
        ArrayList types = new ArrayList();
	string currentInterface = "";

        void ProcessProperty(string line)
        {
            bool ro = false;
            string getter = null;
            string assign = "";

            line = CleanDeclaration(line);
            if (line.Length == 0)
                return;

            int p = line.IndexOf(')');
            var sub = line.Substring(0, p + 1);
            if (sub.IndexOf("readonly") != -1)
            {
                ro = true;
            }
            int j = sub.IndexOf("getter=");
            if (j != -1)
            {
                int k = sub.IndexOfAny(new char[] { ',', ')' }, j + 1);
                Console.WriteLine("j={0} k={1} str={2}", j, k, sub);
                getter = sub.Substring(j + 7, k - (j + 7));
            }
            if (sub.IndexOf("assign") != -1)
                assign = ", ArgumentSemantic.Assign";
            if (sub.IndexOf("retain") != -1)
                assign += ", ArgumentSemantic.Retain";
            if (sub.IndexOf("copy") != -1)
                assign += ", ArgumentSemantic.Copy";
            if (sub.IndexOf("readonly") != -1)
                assign += ", ArgumentSemantic.ReadOnly";

            var type = new StringBuilder();
            int i = p + 1;
            for (; i < line.Length; i++)
            {
                char c = line[i];
                if (!Char.IsWhiteSpace(c))
                    break;
            }
            for (; i < line.Length; i++)
            {
                char c = line[i];
                if (Char.IsWhiteSpace(c))
                    break;
                type.Append(c);
            }

            for (; i < line.Length; i++)
            {
                char c = line[i];
                if (Char.IsWhiteSpace(c) || c == '*')
                    continue;
                else
                    break;
            }
            var selector = new StringBuilder();
            for (; i < line.Length; i++)
            {
                char c = line[i];
                if (Char.IsWhiteSpace(c) || c == ';')
                    break;
                selector.Append(c);
            }
            string retType = TranslateType(type.ToString()).Trim();
            if (retType == "id")
                retType = currentInterface;

            gencs.WriteLine("\t//{0}", line);
            gencs.WriteLine("\t[Export (\"{0}\"{1})]", selector, assign);
            gencs.WriteLine("\t{0} {1} {{ {2} {3} }}",
                     retType, selector.ToString().Substring(0, 1).ToUpper() + selector.ToString().Substring(1, selector.Length-1),
                     getter != null ? "[Bind (\"" + getter + "\")] get;" : "get;",
                     ro ? "" : "set; ");
            gencs.WriteLine();
        }

        string MakeSelector(string sig)
        {
            StringBuilder sb = new StringBuilder();
            string[] split1 = sig.Split(':');
            int numArguments = split1.Length - 1;
            if (numArguments > 0)
            {
                string[] args = new string[numArguments];
                //split[0] is the function name
                //split[1] is the first argument plus the name of the 2nd argument if present
                for (int a = 1; a < split1.Length; a++)
                {
                    //(@"(?<=\().*?[nNsS].*?(?=\))", 
                    string r = Regex.Replace(split1[a], "\\(.+\\)", "").Trim();
                    string[] split11 = r.Split(new char[2] { ' ', '\t' });
                    if (split11.Length == 2)
                    {
                        if (string.IsNullOrEmpty(args[a-1]) == true)
                            args[a - 1] = split11[0];
                        if (a >= numArguments)
                            continue;
                        args[a] = split11[1] + ":";
                    }
                    else if (split11.Length == 1 && a == 1)
                        args[a - 1] = split11[0] + ":";
                }
                string signature = split1[0] + ":";
                for (int b = 1; b < args.Length; b++)
                    signature += args[b];
                return signature.Replace(";","").Trim();
            }
            else
                return sig.Replace(";", "").Trim();

            for (int i = 0; i < sig.Length; i++)
            {
                char c = sig[i];
                if (c == ' ')
                    continue;
                if (c == ';')
                    break;
                else if (c == ':')
                {
                    bool bSkipFirst = true;
                    sb.Append(c);
                    i++;
                    for (; i < sig.Length; i++)
                    {
                        c = sig[i];
                        if (c == ')')
                        {
                            if (bSkipFirst)
                            {
                                bSkipFirst = false;
                                continue;
                            }
                            for (++i; i < sig.Length; i++)
                            {
                                if (!Char.IsLetterOrDigit(sig[i]))
                                    break;
                            }
                            break;
                        }
                    }
                }
                else
                    sb.Append(c);
            }
            return sb.ToString();
        }

        enum State
        {
            SkipToType,
            EndOfType,
            Parameter,

        }

        string MakeParameters(string sig)
        {
            Console.WriteLine("[{0}]", sig);

            int colon = sig.IndexOf(':');
            if (colon == -1)
                return "";
            string line = "";
            string[] s = sig.Replace(";", "").Split(':');
            for (int x = 1; x < s.Length; x++)
            {
                int stringTextReplacement = 0;

                string[] b = s[x].Split(')');
                if (string.IsNullOrEmpty(line) == false)
                    line += ", ";

                if (b.Length == 2)
                {
	            string t = TranslateType(b[0].Replace("(","").Replace(")", ""));
		    if (t == "id")
		        t = "NSObject";
                    string[] c = b[1].Trim().Split(new char[2] { ' ', '\t' });
                    if (c[0] == "string" || c[0] == "event" || c[0] == "delegate" || c[0] == "object")
                        c[0] = c[0] + (++stringTextReplacement).ToString();
                    line += t + " " + c[0];
                }
                else
                    line += b[0];
            }
            return line;

            var sb = new StringBuilder();

            State state = State.SkipToType;
            for (int i = 0; i < sig.Length; i++)
            {
                char c = sig[i];

                switch (state)
                {
                    case State.SkipToType:
                        if (Char.IsWhiteSpace(c))
                            continue;
                        if (c == '(')
                            state = State.EndOfType;
                        break;
                    case State.EndOfType:
                        if (c == ')')
                        {
                            state = State.Parameter;
                            sb.Append(' ');
                        }
                        else
                        {
                            if (c != '*')
                                sb.Append(c);
                        }
                        break;

                    case State.Parameter:
                        if (Char.IsWhiteSpace(c))
                        {
                            state = State.SkipToType;
                            sb.Append(", ");
                        }
                        else
                        {
                            if (c != ';')
                                sb.Append(c);
                        }
                        break;
                }

            }

            //Console.WriteLine ("{0}", sb);
            return sb.ToString();
        }

        Regex rx = new Regex("__OSX_AVAILABLE_STARTING\\(.*\\)");

        string CleanDeclaration(string line)
        {
            return rx.Replace(line, "");
        }
        string TranslateType(string retval)
        {
            retval = retval.Trim();

            if (retval.Contains("<"))
            {
                string[] a = retval.Split('<');
                retval = a[0].Trim();
            }

            switch (retval)
            {
                case "void *":
                case "void*":
                    retval = "IntPtr";
                    break;
                case "NSMutableArray*":
                case "NSMutableArray *":
                case "NSMutableArray":
		case "NSArray*":
		case "NSArray *":
		case "NSArray":
                    retval = "NSObject[]";
                    break;
                case "NSMutableSet":
                    retval = "NSSet";
                    break;
                case "NSMutableDictionary":
                    retval = "NSDictionary";
                    break;
                case "NSString":
                case "NSString*":
                case "NSString *":
                    retval = "string";
                    break;
//                case "id":
 //                   retval = "NSObject";
  //                  break;
                case "BOOL":
                    retval = "bool";
                    break;
                case "NSURL":
                    retval = "NSUrl";
                    break;
                case "CFTimeInterval":
                    retval = "double";
                    break;
                case "CGRect":
                    retval = "System.Drawing.RectangleF";
                    break;
                case "CGPoint":
                    retval = "System.Drawing.PointF";
                    break;
                case "CGSize":
                    retval = "System.Drawing.SizeF";
                    break;
                case "CGFloat":
                case "GLfloat":
                    retval = "float";
                    break;
                case "GLubyte":
                    retval = "byte";
                    break;
                case "ccTime":
                    retval = "float";
                    break;
                case "GLshort":
                    retval = "short";
                    break;
                case "unsigned int":
                case "NSUInteger":
                case "GLenum":
		case "GLuint":
                    retval = "uint";
                    break;
		case "NSInteger":
                    retval = "int";
                    break;
                case "unsigned char":
                    retval = "byte";
                    break;
                case "SEL":
                    retval = "Selector";
                    break;
                case "NSTimeInterval":
                    retval = "double";
                    break;
            }

            return retval.Replace("*","");
        }
        void ProcessDeclaration(bool isProtocol, string line, bool is_optional)
        {
            line = CleanDeclaration(line);
            if (line.Length == 0)
                return;

            if (line.Contains("deprecated") || line.Contains("NS_REQUIRES_NIL_TERMINATION"))
            {
                gencs.WriteLine("\t//{0}", line);
                return;
            }

            if (isProtocol && !is_optional)
            {
                gencs.WriteLine("\t[Abstract]");
            }
            if (line.StartsWith("@property"))
            {
                ProcessProperty(line);
                return;
            }
            if (line.StartsWith("typedef enum"))
            {
                ProcessEnum(line);
                return;
            }
            if (line.StartsWith("typedef struct"))
            {
                ProcessStruct(line);
                return;
            }

            Console.WriteLine("PROCESSING: {0}", line);
            if (line.StartsWith("+"))
                gencs.WriteLine("\t[Static]");
            int p, q;
            p = line.IndexOf('(');
            if (p == -1)
                return;
            q = line.IndexOf(')');

            if (line.Contains("float*") == true)
                q = q;
            //Console.WriteLine ("->{0}\np={1} q-p={2}", line, p, q-p);
            string retval = line.Substring(p + 1, q - p - 1);
            retval = TranslateType(retval);

	    if (retval == "id")
		retval = currentInterface;

            p = line.IndexOf(';');
            string signature = line.Substring(q + 1, p - q);
            string selector = MakeSelector(signature);
            string parameters = MakeParameters(signature);

            Console.WriteLine("signature: {0}", signature);
            Console.WriteLine("selector: {0}", selector);
            gencs.WriteLine("\t//{0}", line);
            gencs.WriteLine("\t[Export (\"{0}\")]", selector);
            int f = selector.IndexOf(':');
            string func = "";
            if (f != -1)
                func = selector.Substring(0, 1).ToUpper() + selector.Substring(1, f - 1);
            else
                func = selector.Substring(0, 1).ToUpper() + selector.Substring(1, selector.Length - 1);
            gencs.WriteLine("\t{0} {1} ({2});", retval, func, parameters);

            gencs.WriteLine();
        }

        private struct ttt
        {
            int x;
        }

        private void ProcessStruct(string line)
        {
            string firstLine = "public struct {0}\n";
            StringBuilder sb = new StringBuilder();
            sb.Append("{\n");
            while (true)
            {
                line = r.ReadLine();
                if (line.Contains("}"))
                {
                    line = line.Replace("}", "").Replace(";", "");
                    firstLine = string.Format(firstLine, line);
                    sb.Insert(0, firstLine);
                    sb.Append("}\n");
                    break;
                }
                else
                {
                    line = line.Trim();
                    if (line == "" || line == "{")
                        continue;
                    sb.Append("\t" + line + "\n");
                }
            }
            enumcs.Write(sb);
        }
        private void ProcessEnum(string line)
        {
            string firstLine = "public enum {0}\n";
            StringBuilder sb = new StringBuilder();
            sb.Append("{\n");
            while (true)
            {
                line = r.ReadLine();
                if (line.Contains(";"))
                {
                    line = line.Replace("}", "").Replace(";", "");
                    firstLine = string.Format(firstLine, line);
                    sb.Insert(0, firstLine);
                    sb.Append("}\n");
                    break;
                }
                else
                {
                    line = line.Trim();
                    if (line == "")
                        continue;
                    sb.Append("\t" + line + "\n");
                }
            }
            enumcs.Write(sb);
        }

        void ProcessInterface(string iface)
        {
            var cols = iface.Split(':');
            string line;

            string className = cols[0].Replace("@interface", "").Trim();
            if (className.Contains("("))
            {
                string[] t = className.Split('(');
                if (types.Contains(t[0].Trim()))
                    return;
            }
            types.Add(className);
	    currentInterface = className;

            Console.WriteLine("**** {0} ", iface);

            if (cols.Length == 2)
            {
                string[] typeLine = cols[1].Trim().Split(new char[] { ' ', ',', '\t', '<' });
                gencs.WriteLine("\n[BaseType (typeof ({0}))]", typeLine[0]);
            }
            gencs.WriteLine("interface {0} {{", className);
            while ((line = r.ReadLine()) != null && !line.StartsWith("@end"))
            {
                string full = "";

                while ((line = r.ReadLine()) != null && !line.StartsWith("@end"))
                {
                    full += line;
                    if (full.IndexOf(';') != -1 || full.IndexOf('}') != -1)
                    {
                        full = full.Replace('\n', ' ');
                        ProcessDeclaration(false, full, false);
                        full = "";
                    }
                }
                break;
            }
            gencs.WriteLine("}");
        }

        void ProcessProtocol(string proto)
        {
            string[] d = proto.Split(new char[] { ' ', '<', '>' });
            string line;

            types.Add(d[1]);
            gencs.WriteLine("\n[BaseType (typeof ({0}))]", d[3]);
            gencs.WriteLine("[Model]");
            gencs.WriteLine("interface {0} {{", d[1]);
            bool optional = false;
            //while ((line = r.ReadLine()) != null && !line.StartsWith("@end"))
            {
                line = r.ReadLine();
                if (line.StartsWith("@optional"))
                    optional = true;

                string full = "";
                while ((line = r.ReadLine()) != null && !line.StartsWith("@end"))
                {
                    full += line;
                    if (full.IndexOf(';') != -1)
                    {
                        full = full.Replace('\n', ' ');
                        ProcessDeclaration(true, full, optional);
                        full = "";
                    }
                }
            }
            gencs.WriteLine("}");
        }

        internal TrivialParser() { }

        internal void Run(string[] args)
        {
            gencs = File.CreateText("gen.cs");
            enumcs = File.CreateText("enum.cs");

            gencs.WriteLine("using MonoTouch.Foundation;");
            gencs.WriteLine("using MonoTouch.UIKit;");
            gencs.WriteLine("using System.Drawing;");
            gencs.WriteLine("using MonoTouch.ObjCRuntime;");
            gencs.WriteLine("using System;");
            gencs.WriteLine("using MonoTouch.CoreGraphics;");
            gencs.WriteLine("\nnamespace {0}", "Cocos2D");
            gencs.WriteLine("{");

            enumcs.WriteLine("\nnamespace {0}", "Cocos2D");
            enumcs.WriteLine("{");

            foreach (string f in args)
            {
                using (var fs = File.OpenRead(f))
                {
                    FileInfo fi = new FileInfo(f);

                    r = new StreamReader(new SourceStream(fs));
                    string line;
                    while ((line = r.ReadLine()) != null)
                    {
                        if (line.StartsWith("#"))
                            continue;
                        if (line.Length == 0)
                            continue;
                        if (line.StartsWith("@class"))
                            continue;

                        if (line.StartsWith("@interface"))
                            ProcessInterface(line);
                        if (line.StartsWith("@protocol") && line.IndexOf("<") != -1)
                            ProcessProtocol(line);
                        if (line.StartsWith("typedef enum"))
                        {
                            ProcessEnum(line);
                            continue;
                        } 
                        if (line.StartsWith("typedef struct"))
                        {
                            ProcessStruct(line);
                            continue;
                        }
                    }
                }
            }
            foreach (string s in types)
            {
                Console.WriteLine("\t\ttypeof ({0}),", s);
            }
            gencs.WriteLine("}");
            enumcs.WriteLine("}");

            gencs.Close();
            enumcs.Close();
        }

        public static void Main (string [] args)
        {
                var tp = new TrivialParser ();
                tp.Run (args);
        }
    }
}

